#include "utilities.h"
#include <openssl/sha.h> /* please read this */
#ifndef min
#define min(A,B) (((A)>(B)) ? (B) : (A))
#endif /* ~min */

int printFlag = 0;
int signalIntFlag = 0;

pthread_t acceptThread;
pthread_t sig_thr_id;
pthread_t eventDisp;
pthread_t keepAliveThread;
pthread_t commandLine;
pthread_t timerThr;
pthread_mutex_t notifyLock;
int notifyCount = 0;
bool checkFlag = false;
bool minNeighFlag = false;
int globalAcceptSock = -1;

pthread_mutex_t statusRespLock;
pthread_mutex_t storelock;
int minNeighCount = 0;
int getIndex = 0;
bool notifyFlag = false;
bool joinFlag = false;
double globalStatusTime = -1;
double globalSearchTime = -1;
double checkResponseTime = -1;
int joinCount = 0;
int currentCacheSize = 0;
int globalFileCounter = 1;
list<storeListStr> myStoreList;
list<fromUOIDStr> myFromUOIDList;
list<uoidStr> myUOIDList;
map<int,beaconmap> myMap;
map<int, allTimers> myTimers;
unsigned int port_recieve = 0;
unsigned int port_timetolive = 0;
char port_recieve_str[512];
char hostname[512];
char node_id[512];
char node_instance_id[512];
char statusFile[128];
char getFile[128];
char input_line[512];
hello_msg myHello;
pthread_mutex_t maplock;
pthread_mutex_t timerlock;
pthread_mutex_t uoidlock;
pthread_mutex_t loglock;
pthread_mutex_t storeFilesLock;
pthread_mutex_t searchFileIdsLock;
pthread_mutex_t searchResultLock;
pthread_mutex_t getIndexLock;
pthread_mutex_t cacheSizeLock;
pthread_mutex_t fileCountsLock;
pthread_mutex_t LRULock;

multimap<int, int> statusResp;
map<string, Values> initMap;
map<int,bool> joinRequestMap;
list<statusSTR> statusResponseList;
list<searchSTR> searchResponseList;
list<getSTR> getResponseList;
list<int> LRUList;
bool isBeacon = false;
bool resetFlag = false;
list<beacon> allNeighbors;
list<beacon>::iterator itAllNeighbors;
list<statusFilesData> statusFilesDataList;
list<search_fileId_results> searchFileIdsList;
list<getSearchResults> searchResultList;

//dispatcher heart queue
list<listBuffStruct> eventQueue;
pthread_mutex_t eventQueueMutex;

int nodeIndex = 0;
uint8_t statType;

map<int, writeListData> writeMap;
pthread_mutex_t writeMapMutex;

multimap<int, neighborStruct> neighborMap;
multimap<int, neighborStruct>::iterator itNeighborMap;

bool dieChild = false;
bool serverShutdown = false;

void generateBitVector(char bitsArray[], char (*word)[128], int noOfWords)
{
	int trailingBits = -1;
	int leadingBits = -1;
	unsigned int last1, last2;
	bool wordNoMatch = true;
	for(int j=0; j<noOfWords; j++)
	{
		unsigned char md[MD5_DIGEST_LENGTH];
		unsigned char sha[SHA_DIGEST_LENGTH];
		char data[512];
		strcpy(data,word[j]);
		wordNoMatch = true;
		for(int l=j+1; l<noOfWords; l++)
		{
			if(strcmp(data, word[l]) == 0)
			{
				wordNoMatch = false;
			}
		}
		
		if(wordNoMatch)
		{
			MD5((unsigned char*)data, strlen(data), md);
			SHA1((unsigned char*)data, strlen(data), sha);
			
			last1 = sha[SHA_DIGEST_LENGTH-1];
			last2 = sha[SHA_DIGEST_LENGTH-2];
			if(last2%2 != 0)
			{
				last2 = 256;
			}
			else
			{
				last2 = 0;
			}
			leadingBits = 512+last2+last1;
			bitsArray[255-(leadingBits/4)] = bitsArray[255-(leadingBits/4)] + pow(2,leadingBits%4);
			
			last1 = md[MD5_DIGEST_LENGTH-1];
			last2 = md[MD5_DIGEST_LENGTH-2];
			if(last2%2 != 0)
			{
				last2 = 256;
			}
			else
			{
				last2 = 0;
			}
			trailingBits = last2+last1;
			bitsArray[255-(trailingBits/4)] = bitsArray[255-(trailingBits/4)] + pow(2,trailingBits%4);
		}
	}
}

int findInUOIDList(char* msgUOID)
{
	list<uoidStr>::iterator it;
	uoidStr tempUOIDStr;
	int n = -1;
	pthread_mutex_lock(&uoidlock);
	for ( it=myUOIDList.begin() ; it != myUOIDList.end(); it++ )
	{
		tempUOIDStr = *it;
		n = memcmp(msgUOID,tempUOIDStr.msgUOID,20);
		if(n==0)
		{
			pthread_mutex_unlock(&uoidlock);
			return 1;
		}
	} 
	pthread_mutex_unlock(&uoidlock);
	return 0;
}

void write_to_log(listBuffStruct lbtemp,int logtype,struct timeval exectime)
{
	FILE * pFile;
	char filepath[512];
	char message[512];
	char data[512];
	memset(message,'\0',512);
	memset(data,'\0',512);
	strcpy(filepath,initMap["HomeDir"].asStr);
	strcat(filepath,"/");
	strcat(filepath,initMap["LogFilename"].asStr);
	pFile = fopen(filepath,"a");
	if(pFile == NULL)
	{
		if(printFlag == 1)
		{
			cout<<"no file"<<endl;
		}
		return;
	}
	msg_header logHdr;
	fillMsgHdrStructure(logHdr,lbtemp.listBuff);
	char msgTypeForm[5];
	if(logHdr.msgtype==(uint8_t)HLLO)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"HLLO");	
	}
	else if(logHdr.msgtype==(uint8_t)STOR)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"STOR");
	}
	else if(logHdr.msgtype==(uint8_t)SHRQ)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"SHRQ");
	}
	else if(logHdr.msgtype==(uint8_t)SHRS)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"SHRS");
	}
	else if(logHdr.msgtype==(uint8_t)GTRQ)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"GTRQ");
	}
	else if(logHdr.msgtype==(uint8_t)GTRS)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"GTRS");
	}
	else if(logHdr.msgtype==(uint8_t)DELT)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"DELT");
	}
	else if(logHdr.msgtype==(uint8_t)STRQ)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"STRQ");
	}
	else if(logHdr.msgtype==(uint8_t)STRS)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"STRS");
	}
	else if(logHdr.msgtype==(uint8_t)KPAV)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"KPAV");
	}
	else if(logHdr.msgtype==(uint8_t)JNRQ)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"JNRQ");
	}
	else if(logHdr.msgtype==(uint8_t)JNRS)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"JNRS");
	}
	else if(logHdr.msgtype==(uint8_t)NTFY)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"NTFY");
	}
	else if(logHdr.msgtype==(uint8_t)CKRS)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"CKRS");
	}
	else if(logHdr.msgtype==(uint8_t)CKRQ)
	{
		memset(msgTypeForm,'\0',5);
		strcpy(msgTypeForm,"CKRQ");
	}
	
	if(logHdr.msgtype==(uint8_t)HLLO)
	{
		uint16_t portnumber;
		char localhostname[512];
		memset(localhostname,'\0',512);
		memcpy(&portnumber,&(lbtemp.listBuff[27]),sizeof(uint16_t));
		memcpy(localhostname,&(lbtemp.listBuff[29]),logHdr.datalength-sizeof(uint16_t));
		sprintf(data,"%d %s",portnumber,localhostname);
	}
	if(logHdr.msgtype==(uint8_t)STRQ)
	{
		uint8_t statustype;
		memcpy(&statustype,&(lbtemp.listBuff[27]),sizeof(uint8_t));
		sprintf(data,"0x%02x",statustype);
	}
	if(logHdr.msgtype==(uint8_t)STRS)
	{
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[43]),4);
		sprintf(data,"%02x%02x%02x%02x",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]));
	}
	if(logHdr.msgtype==(uint8_t)KPAV)
	{
	
	}
	if(logHdr.msgtype==(uint8_t)STOR)
	{
	
	}
	if(logHdr.msgtype==(uint8_t)SHRQ)
	{
		uint8_t searchtype;
		memcpy(&searchtype,&(lbtemp.listBuff[27]),sizeof(uint8_t));
		sprintf(data,"%d",searchtype);
	}
	if(logHdr.msgtype==(uint8_t)SHRS)
	{
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[43]),4);
		sprintf(data,"%02x%02x%02x%02x",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]));
	}
	if(logHdr.msgtype==(uint8_t)GTRQ)
	{
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[43]),4);
		sprintf(data,"%02x%02x%02x%02x",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]));
	}
	if(logHdr.msgtype==(uint8_t)GTRS)
	{
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[43]),4);
		sprintf(data,"%02x%02x%02x%02x",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]));
	}
	if(logHdr.msgtype==(uint8_t)NTFY)
	{
		uint8_t type;
		memcpy(&type, &(lbtemp.listBuff[27]), sizeof(uint8_t));
		sprintf(data,"%d", type);
	}
	if(logHdr.msgtype==(uint8_t)JNRQ)
	{
		uint16_t portnumber;
		char localhostname[512];
		memset(localhostname,'\0',512);
		memcpy(&portnumber,&(lbtemp.listBuff[31]),sizeof(uint16_t));
		memcpy(localhostname,&(lbtemp.listBuff[33]),logHdr.datalength-sizeof(uint16_t)-sizeof(uint32_t));
		sprintf(data,"%d %s",portnumber,localhostname);
	}
	if(logHdr.msgtype==(uint8_t)JNRS)
	{
		uint16_t portnumber;
		uint32_t distance;
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[43]),4);
		char localhostname[512];
		memset(localhostname,'\0',512);
		memcpy(&distance,&(lbtemp.listBuff[47]),sizeof(uint32_t));
		memcpy(&portnumber,&(lbtemp.listBuff[51]),sizeof(uint16_t));
		memcpy(localhostname,&(lbtemp.listBuff[53]),lbtemp.datalength-27-sizeof(uint16_t)-sizeof(uint32_t));
		sprintf(data,"%02x%02x%02x%02x %d %d %s",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]),distance, portnumber,localhostname);
	}
	if(logHdr.msgtype==(uint8_t)CKRQ)
	{
	}
	if(logHdr.msgtype==(uint8_t)CKRS)
	{
		char lastuoid[4];
		memset(lastuoid,'\0',4);
		memcpy(lastuoid,&(lbtemp.listBuff[27]),4);
		sprintf(data,"%02x%02x%02x%02x",(uint8_t)(lastuoid[0]),(uint8_t)(lastuoid[1]),(uint8_t)(lastuoid[2]),(uint8_t)(lastuoid[3]));
	}
	if(logtype==0)
	{
		sprintf(message,"r %10ld.%03d %s_%d %s %d %d %02x%02x%02x%02x %s\n",exectime.tv_sec,(int)exectime.tv_usec/1000,hostname,lbtemp.portNumber,msgTypeForm,lbtemp.datalength,logHdr.timetolive,(uint8_t)(logHdr.uoid[16]),(uint8_t)(logHdr.uoid[17]),(uint8_t)(logHdr.uoid[18]),(uint8_t)(logHdr.uoid[19]),data);
		//fputs ("r <time> <from> <msgtype> <size> <ttl> <msgid> <data>\n",pFile);    
		fputs (message,pFile);   
	}
	else if(logtype==1)
	{
		sprintf(message,"s %10ld.%03d %s_%d %s %d %d %02x%02x%02x%02x %s\n",exectime.tv_sec,(int)exectime.tv_usec/1000,hostname,lbtemp.portNumber,msgTypeForm,lbtemp.datalength,logHdr.timetolive,(uint8_t)(logHdr.uoid[16]),(uint8_t)(logHdr.uoid[17]),(uint8_t)(logHdr.uoid[18]),(uint8_t)(logHdr.uoid[19]),data);
		//fputs ("s <time> <from> <msgtype> <size> <ttl> <msgid> <data>\n",pFile);
		fputs (message,pFile);
	}
	else if(logtype==2)
	{
		sprintf(message,"f %10ld.%03d %s_%d %s %d %d %02x%02x%02x%02x %s\n",exectime.tv_sec,(int)exectime.tv_usec/1000,hostname,lbtemp.portNumber,msgTypeForm,lbtemp.datalength,logHdr.timetolive,(uint8_t)(logHdr.uoid[16]),(uint8_t)(logHdr.uoid[17]),(uint8_t)(logHdr.uoid[18]),(uint8_t)(logHdr.uoid[19]),data);
		//fputs ("f <time> <from> <msgtype> <size> <ttl> <msgid> <data>\n",pFile);
		fputs (message,pFile);
	}
	fclose(pFile);
}

char *GetUOID(char *node_inst_id, char *obj_type, char *uoid_buf, int uoid_buf_sz)
{
	static unsigned long seq_no=(unsigned long)1;
	char sha1_buf[SHA_DIGEST_LENGTH], str_buf[104];
	snprintf(str_buf, sizeof(str_buf), "%s_%s_%1ld",node_inst_id, obj_type, (long)seq_no++);
	SHA1((const unsigned char*)str_buf, strlen(str_buf),(unsigned char*)sha1_buf);
	memset(uoid_buf, 0, uoid_buf_sz);
	memcpy(uoid_buf, sha1_buf,min((unsigned int)uoid_buf_sz,sizeof(sha1_buf)));
	return uoid_buf;
}

void createMsgHdrStr(msg_header *msg, uint8_t msgtype,uint8_t timetolive,char* obj,char* node_inst_id,uint32_t msgsize)
{
	char* str;
	char buf[SHA_DIGEST_LENGTH];

	str = GetUOID(node_inst_id,obj, buf, sizeof(buf));
	
	msg->msgtype = (uint8_t)msgtype;
	memcpy(msg->uoid,buf,20);
	msg->timetolive = timetolive;
	msg->reserved = 0;
	memcpy(&(msg->datalength), &msgsize, sizeof(uint32_t)); 
}
 
void remAllWhiteSpaces(char *str)
{
	unsigned int i = 0;
	char *ptr = str;
	for(i = 0; i < strlen(str) || *ptr == '\0'; )
	{
		if(*ptr == ' ')
		{
			memcpy(ptr, ptr + 1, strlen(ptr)+1);
			continue;
		}
		else
		{
			i++;
			ptr++;
		}
	}
}

void parserInitNeighFile()
{
	char file[256];
	char line[256];
	char *findColan;
	
	memset(file, '\0', 256);
	strcpy(file, initMap["HomeDir"].asStr);
	strcat(file, "/init_neighbor_list");
	FILE* fp = fopen(file,"rb");
	if(fp == NULL)
	{
		if(printFlag==1)
		{
			cout<<"File does not exist"<<endl;
		}
		exit(1);
	}
	while(fgets(line,sizeof(line),fp))
	{
		findColan = strchr(line, ':');
		if(findColan != NULL)
		{
			beacon bec;
			char *value;
			value = strtok(line, ":");
			strcpy(bec.hostName, value);
			while(value != NULL)
			{
				value = strtok(NULL, ":");
				if(value != NULL)
				{
					bec.port = atoi(value);
				}
			}
			allNeighbors.push_back(bec);
		}
	}
}

void parser_function(int argc, char *argv[])
{
	char stringBuf[100];
	char empty_string[100];
	memset(&stringBuf, '\0', sizeof("servant.log"));
	strcpy(stringBuf, "servant.log");
	strcpy(empty_string, "");
	initMap["Port"] = (double)-1;
	initMap["Location"] = (double)-1;
	initMap["HomeDir"] = empty_string;
	initMap["LogFilename"] = stringBuf;
	initMap["AutoShutdown"] = (double)900;
	initMap["TTL"] = (double)30;
	initMap["MsgLifetime"] = (double)60;
	initMap["GetMsgLifetime"] = (double)300;
	initMap["InitNeighbors"] = (double)3;
	initMap["JoinTimeout"] = (double)15;
	initMap["KeepAliveTimeout"] = (double)60;
	initMap["MinNeighbors"] = (double)2;
	initMap["CacheProb"] = (double)0.1;
	initMap["StoreProb"] = (double)0.1;
	initMap["NeighborStoreProb"] = (double)0.2;
	initMap["CacheSize"] = (double)500;
	initMap["NoCheck"] = (double)0;
	initMap["Retry"] = (double)30;
	
	int checkBeaconPort;
	char *beaconHostName;
	
	char *file;
	char line[255];
	char *findEquals;
	char *findColan;
	char *findSemicolan;
	char *keyValue;
	char key[100];
	string KEY;
	
	argc--, argv++;
	for (; argc > 0; argc--, argv++) 
	{
		if (*argv[0] == '-') 
		{
			if (strcmp(*argv, "-reset") == 0) 
			{
				resetFlag = true;
			}
		}
		else
		{
			//store the input file name
			file = *argv;
			FILE* fp = fopen(file,"rb");
			if(fp == NULL)
			{
				if(printFlag==1)
				{
					cout<<"File does not exist"<<endl;
				}
				exit(1);
			}
			while(fgets(line,sizeof(line),fp))
			{
				//First remove the whitespaces in the line
				remAllWhiteSpaces(line);
				//Find '=' which says its a key value pair
				findEquals = strchr(line, '=');
				if(findEquals == NULL)
				{
					continue;
				}
				else
				{
					//Find ';' then its a comment so ignore even if '=' exists
					findSemicolan = strchr(line, ';');
					if(findSemicolan == NULL)
					{
						//find for ':' signifies beacons else init variables
						findColan = strchr(line, ':');
						if(findColan == NULL)
						{
							keyValue = strtok(line, "=");
							memset(&key, '\0', sizeof(keyValue));
							strcpy(key, keyValue);
							KEY.assign(key);
							while(keyValue != NULL)
							{
								keyValue = strtok(NULL,"=");
								if(keyValue != NULL)
								{
									keyValue[strlen(keyValue)-1] = '\0';
									if(KEY.compare("HomeDir") == 0||KEY.compare("LogFilename") == 0)
									{
										char homeBuf[100];
										memset(&homeBuf, '\0', sizeof(keyValue));
										strcpy(homeBuf, keyValue);
										initMap[KEY] = homeBuf;
									}
									else
									{
										initMap[KEY] = atof(keyValue);
										
									}
								}
							}
						}
						else
						{
							beacon bec;
							beaconHostName = strtok(line, "=");
							if(beaconHostName != NULL)
							{
								keyValue = strtok(beaconHostName, ":");
								strcpy(bec.hostName, keyValue);
								while(keyValue != NULL)
								{
									keyValue = strtok(NULL, ":");
									if(keyValue != NULL)
									{
										checkBeaconPort = atoi(keyValue);
										bec.port = checkBeaconPort;
										if(initMap["Port"].asDouble == checkBeaconPort)
										{
											isBeacon = true;
										}
									}
								}
							}
							allNeighbors.push_back(bec);
						}
					}
					else
					{
						continue;
					}
				}
			}
			fclose(fp);
		}
	}
}

char *toLowerCase(char *str) 
{
	char *newStr, *iter;
	iter = newStr = (char *)malloc(strlen(str));
	strcpy(newStr, str);
	while (*iter) 
	{
		char c = *newStr;
		if (c >= 'A' && c <= 'Z')
		*iter = c - 'A' + 'a';
		++iter;
	}
	return newStr;
}

storeListStr storeFunctionality(char (*Keywords)[128],char filename[],int keycount)
{
	storeListStr storeData;
	int sz=0;
	int k = 0;
	struct stat st;
	k = stat(filename, &st);
	sz = st.st_size;
	memcpy(&storeData.fileSize,&sz,sizeof(int));
	char ch;
	FILE * pFile;
	pFile=fopen (filename,"r");
	SHA_CTX *c = new SHA_CTX;
	unsigned char *md= new unsigned char[20];
	unsigned int j=0;
	SHA1_Init(c);
	for(j=0;(int)j<sz;j++)
	{
	fread (&ch,1,1,pFile);
	SHA1_Update(c,&ch,1);
	}
	fclose(pFile);
	SHA1_Final(md,c);
	char bitsArray[256]={0};
	generateBitVector(bitsArray,Keywords,keycount);
	
	char* str1;
	char buf[SHA_DIGEST_LENGTH];
	char noncebuf[SHA_DIGEST_LENGTH];
	char obj[2];
	obj[0]='\0';
	char xTemp[20][2];
	char yTemp[40];
	for(j=0;j<20;j++)
	{
		memset(xTemp[j],'\0',2);
	}
	for(j=0;j<20;j++)
	{
		sprintf(xTemp[j],"%02x",md[j]);
	}
	for(j=0;j<20;j++)
	{
		memcpy(&(yTemp[2*j]),xTemp[j],2);
	}
	time_t seconds;
	char newTemp[200];
	memset(newTemp,'\0',200);
	seconds = time(NULL);
	sprintf(newTemp, "%s_%ld", yTemp, seconds);
	str1 = GetUOID(newTemp,obj,buf,sizeof(buf));
	if(printFlag == 1)
	{
		cout<<strlen(buf)<<endl;
	}
	buf[20]='\0';
	SHA1((const unsigned char*)buf, strlen(buf),(unsigned char*)noncebuf);
	//Create the files folder and storing the data in the files
	//Create the .meta file
	char storefilename[200];
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"files");
	mkdir(storefilename,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	pthread_mutex_lock(&fileCountsLock);
	int index = globalFileCounter;
	char fc[10];
	memset(fc,'\0',10);
	sprintf(fc,"%d",globalFileCounter++);
	pthread_mutex_unlock(&fileCountsLock);
	strcat(storefilename,"/");
	strcat(storefilename,fc);
	strcat(storefilename,".meta");
	pFile=fopen(storefilename,"w");
	fprintf(pFile,"[metadata]\n");
	fprintf(pFile,"Filename=%s\n",filename);
	memset(storeData.fileName,'\0',200);
	memcpy(storeData.fileName,filename,strlen(filename));
	fprintf(pFile,"Filesize=%d\n",sz);
	fprintf(pFile,"SHA1=");
	memset(storeData.fileSha1,'\0',20);
	for(j=0;j<20;j++)
	{
		fprintf(pFile,"%02x",md[j]);
		memcpy(&(storeData.fileSha1[j]),&(md[j]),1);
	}
	fprintf(pFile,"\n");
	fprintf(pFile,"Nonce=");
	memset(storeData.fileNonce,'\0',20);
	for(j=0;j<20;j++)
	{
		fprintf(pFile,"%02x",(uint8_t)noncebuf[j]);
		memcpy(&(storeData.fileNonce[j]),&(noncebuf[j]),1);
	}
	fprintf(pFile,"\n");
	fprintf(pFile,"Keywords=");
	for(j=0;(int)j<keycount;j++)
	{
		fprintf(pFile,"%s ",Keywords[j]);
	}
	fprintf(pFile,"\n");
	fprintf(pFile,"Bit-vector=");
	memset(storeData.fileBitVector,'\0',256);
	for(j=0;j<256;j++)
	{
		fprintf(pFile,"%d",bitsArray[j]);
		char temp[2];
		memset(temp, '\0', 2);
		
		sprintf(temp, "%d", bitsArray[j]);
		storeData.fileBitVector[j] = temp[0];
	}
	fprintf(pFile,"\n");
	fclose(pFile);
	memcpy(&storeData.fileIndex,&index,sizeof(int));
	pthread_mutex_lock(&storelock);
	myStoreList.push_back(storeData);
	pthread_mutex_unlock(&storelock);
	//Create the .pass file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"files");
	strcat(storefilename,"/");
	strcat(storefilename,fc);
	strcat(storefilename,".pass");
	pFile=fopen(storefilename,"w");
	fwrite(buf,1,20,pFile);
	fclose(pFile);

	//Create the .data file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"files");
	strcat(storefilename,"/");
	strcat(storefilename,fc);
	strcat(storefilename,".data");
	pFile=fopen(storefilename,"w");
	FILE *pFile1;
	pFile1 = fopen(filename,"r");
	for(j=0;(int)j<(int)sz;j++)
	{
	fread (&ch,1,1,pFile1);
	fwrite(&ch,1,1,pFile);
	}
	fclose(pFile);
	fclose(pFile1);
	//Create the kwrd_index file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"kwrd_index");
	pFile=fopen(storefilename,"a");
	fprintf(pFile,"%d=",index);
	for(j=0;j<256;j++)
	{
		fprintf(pFile,"%d",bitsArray[j]);
	}
	fprintf(pFile,"\n");
	fclose(pFile);

	//Create the name_index file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"name_index");
	pFile=fopen(storefilename,"a");
	fprintf(pFile,"%d=",index);
	fprintf(pFile,"%s",filename);
	fprintf(pFile,"\n");
	fclose(pFile);

	//Create the nonce_index file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"nonce_index");
	pFile=fopen(storefilename,"a");
	fprintf(pFile,"%d=",index);
	for(j=0;j<20;j++)
	{
		fprintf(pFile,"%02x",(uint8_t)noncebuf[j]);
	}
	fprintf(pFile,"\n");
	fclose(pFile);

	//Create the sha1_index file
	strcpy(storefilename,initMap["HomeDir"].asStr);
	strcat(storefilename,"/");
	strcat(storefilename,"sha1_index");
	pFile=fopen(storefilename,"a");
	fprintf(pFile,"%d=",index);
	for(j=0;j<20;j++)
	{
		fprintf(pFile,"%02x",md[j]);
	}
	fprintf(pFile,"\n");
	fclose(pFile);

	return storeData;
}

int hextoint(char c)
{
	c = toupper(c);
	return (c>'9' ? c-'A'+10 : c-'0');
}

void convert40Bto20B(char *byte20, char *byte40)
{
	char *ptr;
	uint8_t temp;
	int i = 0;
	for(ptr = byte40; *ptr; ptr+=2)
	{
		temp = (hextoint(*ptr) << 4) + hextoint(*(ptr+1));
		memcpy(&(byte20[i++]), &temp, sizeof(uint8_t));
	}
}

void searchFiles(search_request searReq)
{
	list<storeListStr>::iterator itMyStoreList;
	char f[128];
	struct timeval t1;
	double tempTime;
	int n = 1;
	if(searReq.searchType == 1)
	{
		pthread_mutex_lock(&storelock);
		for(itMyStoreList = myStoreList.begin(); itMyStoreList != myStoreList.end(); itMyStoreList++)
		{
			if(strcmp(itMyStoreList->fileName, searReq.searchQuery) == 0)
			{
				memset(f, '\0', 128);
				gettimeofday(&t1, NULL);
				tempTime = (double)(t1.tv_sec * (1e+3) + (double)t1.tv_usec/1000);
				sprintf(f, "%s_%lf", itMyStoreList->fileName, tempTime);
				search_fileId_results searFidRes;
				memset(searFidRes.fileUOID, '\0', 20);
				SHA1((const unsigned char*)f, strlen(f), (unsigned char*)searFidRes.fileUOID);
				searFidRes.fileIndex = itMyStoreList->fileIndex;
				pthread_mutex_lock(&searchFileIdsLock);
				searchFileIdsList.push_back(searFidRes);
				pthread_mutex_unlock(&searchFileIdsLock);
				if(printFlag == 1)
				{
					cout<<"fid - ";
					for(int m=0; m<20; m++)
					{
						printf("%02x", (uint8_t)searFidRes.fileUOID[m]);
					}
					cout<<endl;
					cout<<"FileIndex - "<<searFidRes.fileIndex<<endl;
				}
			}
		}
		pthread_mutex_unlock(&storelock);
	}
	else if(searReq.searchType == 2)
	{
		pthread_mutex_lock(&storelock);
		for(itMyStoreList = myStoreList.begin(); itMyStoreList != myStoreList.end(); itMyStoreList++)
		{
			n = -1;
			n = memcmp(searReq.searchQuery, itMyStoreList->fileSha1, 20);
			if(n == 0)
			{
				memset(f, '\0', 128);
				gettimeofday(&t1, NULL);
				tempTime = (double)(t1.tv_sec * (1e+3) + (double)t1.tv_usec/1000);
				sprintf(f, "%s_%lf", itMyStoreList->fileName, tempTime);
				search_fileId_results searFidRes;
				memset(searFidRes.fileUOID, '\0', 20);
				SHA1((const unsigned char*)f, strlen(f), (unsigned char*)searFidRes.fileUOID);
				searFidRes.fileIndex = itMyStoreList->fileIndex;
				pthread_mutex_lock(&searchFileIdsLock);
				searchFileIdsList.push_back(searFidRes);
				pthread_mutex_unlock(&searchFileIdsLock);
				if(printFlag == 1)
				{
					cout<<"fid - ";
					for(int m=0; m<20; m++)
					{
						printf("%02x", (uint8_t)searFidRes.fileUOID[m]);
					}
					cout<<endl;
					cout<<"FileIndex - "<<searFidRes.fileIndex<<endl;
				}
			}
		}
		pthread_mutex_unlock(&storelock);
	}
	else if(searReq.searchType == 3)
	{
		//Generate the Bitvector for the keywords specified and then compare it with the indexes
		char *str;
		char keywords[20][128];
		int keyCount = 0;
		str = strtok(searReq.searchQuery, " ");
		str = toLowerCase(str);
		strcpy(keywords[keyCount++], str);
		while(str != NULL)
		{
			str = strtok(NULL, " ");
			if(str != NULL)
			{
				str = toLowerCase(str);
				strcpy(keywords[keyCount++], str);
			}
		}
		char bitsArray[256]={0};
		generateBitVector(bitsArray,keywords,keyCount);
		
		bool noMatch = false;
		pthread_mutex_lock(&storelock);
		for(itMyStoreList = myStoreList.begin(); itMyStoreList != myStoreList.end(); itMyStoreList++)
		{
			storeListStr temp = *itMyStoreList;
			noMatch = true;
			for(int m=0; m<256; m++)
			{
				if(bitsArray[m] != 0)
				{
					if(temp.fileBitVector[m] != (char)(bitsArray[m]+48))
					{
						noMatch = false;
						break;
					}
				}
			}
			if(noMatch)
			{
				memset(f, '\0', 128);
				gettimeofday(&t1, NULL);
				tempTime = (double)(t1.tv_sec * (1e+3) + (double)t1.tv_usec/1000);
				sprintf(f, "%s_%lf", temp.fileName, tempTime);
				search_fileId_results searFidRes;
				memset(searFidRes.fileUOID, '\0', 20);
				SHA1((const unsigned char*)f, strlen(f), (unsigned char*)searFidRes.fileUOID);
				searFidRes.fileIndex = temp.fileIndex;
				pthread_mutex_lock(&searchFileIdsLock);
				searchFileIdsList.push_back(searFidRes);
				pthread_mutex_unlock(&searchFileIdsLock);
				if(printFlag == 1)
				{
					cout<<"fid - ";
					for(int m=0; m<20; m++)
					{
						printf("%02x", (uint8_t)searFidRes.fileUOID[m]);
					}
					cout<<endl;
					cout<<"FileIndex - "<<searFidRes.fileIndex<<endl;
				}
			}
		}
		pthread_mutex_unlock(&storelock);
	}
}

void displaySearchResults(char *metaFile, char *sha1)
{
	char line[10][512];
	int l = 0;
	char *str;
	str = strtok(metaFile, "\n");
	while(str != NULL)
	{
		str = strtok(NULL, "\n");
		if(str != NULL)
		{
			strcpy(line[l++], str);
		}
	}
	bool shaFound = false;
	for(int k=0; k<6; k++)
	{
		char *str1 = strtok(line[k], "=");
		while(str1 != NULL)
		{
			if((strcmp(str1, "Filename") == 0) || (strcmp(str1, "Filesize") == 0) || (strcmp(str1, "SHA1") == 0) || (strcmp(str1, "Nonce") == 0) || (strcmp(str1, "Keywords") == 0)) 
			{
				shaFound = false;
				if(strcmp(str1, "SHA1") == 0)
				{
					shaFound = true;
				}
				cout<<"    "<<str1<<"=";
				str1 = strtok(NULL, "=");
				if(str1 != NULL)
				{
					cout<<str1<<endl;
					if(shaFound)
					{
						char temp[41];
						strcpy(temp, str1);
						temp[40] = '\0';
						convert40Bto20B(sha1, temp);
					}
				}
			}
			else
			{
				str1 = strtok(NULL, "=");
			}
		}
	}
}

void extractNonShaFilN(char *meta, char *fileN, char *sha1, char *nonce)
{
	char line[10][512];
	int l = 0;
	char *str;
	str = strtok(meta, "\n");
	while(str != NULL)
	{
		str = strtok(NULL, "\n");
		if(str != NULL)
		{
			strcpy(line[l++], str);
		}
	}
	for(int k=0; k<6; k++)
	{
		char *str1 = strtok(line[k], "=");
		while(str1 != NULL)
		{
			if((strcmp(str1, "Filename") == 0))
			{
				str1 = strtok(NULL, "=");
				if(str1 != NULL)
				{
					strcpy(fileN, str1);
				}
			}
			else if((strcmp(str1, "SHA1") == 0))
			{
				str1 = strtok(NULL, "=");
				if(str1 != NULL)
				{
					char temp[41];
					strcpy(temp, str1);
					temp[40] = '\0';
					convert40Bto20B(sha1, temp);
				}
			}
			else if((strcmp(str1, "Nonce") == 0))
			{
				str1 = strtok(NULL, "=");
				if(str1 != NULL)
				{
					char temp[41];
					strcpy(temp, str1);
					temp[40] = '\0';
					convert40Bto20B(nonce, temp);
				}
			}
			else
			{
				str1 = strtok(NULL, "=");
			}
		}
	}
}


void uploadStoredFilesList()
{
	FILE *file1, *file2, *file3, *file4;
	char filename[512];
	char file[128];
	int sz=0;
	int k = 0;
	struct stat st;
	memset(filename, '\0', 512);
	strcpy(filename,initMap["HomeDir"].asStr);
	strcat(filename,"/");
	strcat(filename,"sha1_index");
	file1 = fopen(filename, "r");
	memset(filename, '\0', 512);
	strcpy(filename,initMap["HomeDir"].asStr);
	strcat(filename,"/");
	strcat(filename,"kwrd_index");
	file2 = fopen(filename, "r");
	memset(filename, '\0', 512);
	strcpy(filename,initMap["HomeDir"].asStr);
	strcat(filename,"/");
	strcat(filename,"name_index");
	file3 = fopen(filename, "r");
	memset(filename, '\0', 512);
	strcpy(filename,initMap["HomeDir"].asStr);
	strcat(filename,"/");
	strcat(filename,"nonce_index");
	file4 = fopen(filename, "r");
	
	storeListStr storeList;
	
	if(file1 != NULL && file2 != NULL && file3 != NULL && file4 != NULL)
	{
		char buf1[512], buf2[512], buf3[512], buf4[512];
		while(!feof(file1))
		{
			memset(buf1, '\0', 512);
			memset(buf2, '\0', 512);
			memset(buf3, '\0', 512);
			memset(buf4, '\0', 512);
			fgets(buf1, 512, file1);
			char *str;
			str = strchr(buf1, '=');
			if(str != NULL)
			{
				char *str1;
				str1 = strtok(buf1, "=");
				storeList.fileIndex = atoi(str1);
				while(str1 != NULL)
				{
					str1 = strtok(NULL, "=");
					if(str1 != NULL)
					{
						char b4[40];
						strncpy(b4, str1, 40);
						memset(storeList.fileSha1, '\0', 20);
						convert40Bto20B(storeList.fileSha1, b4);
					}
				}
				fgets(buf2, 512, file2);
				char *str2;
				str2 = strtok(buf2, "=");
				while(str2 != NULL)
				{
					str2 = strtok(NULL, "=");
					if(str2 != NULL)
					{
						memset(storeList.fileBitVector, '\0', 256);
						strncpy(storeList.fileBitVector, str2, 256);
					}
				}
				fgets(buf3, 512, file3);
				char *str3;
				str3 = strtok(buf3, "=");
				while(str3 != NULL)
				{
					str3 = strtok(NULL, "=");
					if(str3 != NULL)
					{
						memset(storeList.fileName, '\0', 200);
						strncpy(storeList.fileName, str3, strlen(str3)-1);
					}
				}
				fgets(buf4, 512, file4);
				char *str4 = (char*)malloc(20);
				str4 = strtok(buf4, "=");
				while(str4 != NULL)
				{
					str4 = strtok(NULL, "=");
					if(str4 != NULL)
					{
						char b4[40];
						strncpy(b4, str4, 40);
						memset(storeList.fileNonce, '\0', 20);
						convert40Bto20B(storeList.fileNonce, b4);
					}
				}
				//extract the filesize from .meta file
				memset(file, '\0', 128);
				sprintf(file, "%s/files/%d.data", initMap["HomeDir"].asStr, storeList.fileIndex);
				k = stat(file, &st);
				sz = st.st_size;
				storeList.fileSize = sz;
				pthread_mutex_lock(&storelock);
				myStoreList.push_back(storeList);
				pthread_mutex_unlock(&storelock);
				
				
			}
		}
	}
	fclose(file1);
	fclose(file2);
	fclose(file3);
	fclose(file4);
}

int meta_file_parsing(char* storefilename, int index)
{
	char filename[8192];
	char ch;
	memset(filename,'\0',8192);
	strcpy(filename,storefilename);
	strcat(filename,".data");
	int sz=0;
	int k = 0;
	struct stat st;
	k = stat(filename, &st);
	sz = st.st_size;
	FILE* pFile;
	pFile = fopen(filename,"r");
	SHA_CTX *c = new SHA_CTX;
	unsigned char *md= new unsigned char[20];
	unsigned int j=0;
	SHA1_Init(c);
	for(j=0;(int)j<sz;j++)
	{
		fread (&ch,1,1,pFile);
		SHA1_Update(c,&ch,1);
	}
	fclose(pFile);
	SHA1_Final(md,c);
	memset(filename,'\0',8192);
	strcpy(filename,storefilename);
	strcat(filename,".meta");
	pFile = fopen(filename,"r");
	char inputline[8192];
	char* findEquals;
	char* key;
	char* value;
	storeListStr storeData;
	while(fgets(inputline,sizeof(inputline),pFile))
	{
		remAllWhiteSpaces(inputline);
		//Find '=' which says its a key value pair
		findEquals = strchr(inputline, '=');
		if(findEquals == NULL)
		{
			continue;
		}
		else
		{
			key = strtok(inputline, "=");
			if(strcmp(key,"Filename")==0)
			{
				value = strtok(NULL, "=");
				value[strlen(value)-1] = '\0';						
				memcpy(storeData.fileName,value,strlen(value)+1);
			}
			if(strcmp(key,"Filesize")==0)
			{
				value = strtok(NULL, "=");
				value[strlen(value)-1] = '\0';						
				storeData.fileSize = atoi(value);
			}
			if(strcmp(key,"SHA1")==0)
			{
				value = strtok(NULL, "=");
				value[strlen(value)-1] = '\0';						
				convert40Bto20B(storeData.fileSha1,value);
			}
			if(strcmp(key,"Nonce")==0)
			{
				value = strtok(NULL, "=");
				value[strlen(value)-1] = '\0';						
				convert40Bto20B(storeData.fileNonce,value);
			}
			if(strcmp(key,"Bit-vector")==0)
			{
				value = strtok(NULL, "=");
				value[strlen(value)-1] = '\0';						
				memcpy(storeData.fileBitVector,value,strlen(value));
			}
		}
	}
	fclose(pFile);
	storeData.fileIndex = index;
	if(printFlag == 1)
	{
		for(int j =0;j<20;j++)
		{
			printf("%02x--->%02x\n",(uint8_t)storeData.fileSha1[j],(uint8_t)md[j]);
		}
	}
	if(memcmp(storeData.fileSha1,md,20)!=0)
	{
		//delete .meta .data file and globalFileCounter--
		memset(filename,'\0',8192);
		strcpy(filename,storefilename);
		strcat(filename,".meta");
		if( remove(filename) != 0 )
		perror( "Error deleting file" );
		else
		{
		if(printFlag == 1)
		{
		printf("Temp meta File successfully deleted" );
		}
		}
		memset(filename,'\0',8192);
		strcpy(filename,storefilename);
		strcat(filename,".data");
		if( remove(filename) != 0 )
		perror( "Error deleting file" );
		else
		{
		if(printFlag == 1)
		{
		printf("Temp data File successfully deleted" );
		}
		}
		return 1;
	}
	else
	{
		pthread_mutex_lock(&cacheSizeLock);
		currentCacheSize = currentCacheSize + storeData.fileSize;
		pthread_mutex_unlock(&cacheSizeLock);
		pthread_mutex_lock(&storelock);
		myStoreList.push_back(storeData);
		pthread_mutex_unlock(&storelock);
		
		pthread_mutex_lock(&LRULock);
		LRUList.push_back(storeData.fileIndex);
		pthread_mutex_unlock(&LRULock);
		
		//Create the kwrd_index file
		strcpy(filename,initMap["HomeDir"].asStr);
		strcat(filename,"/");
		strcat(filename,"kwrd_index");
		pFile=fopen(filename,"a");
		fprintf(pFile,"%d=",index);
		for(j=0;j<256;j++)
		{
			fprintf(pFile,"%c",storeData.fileBitVector[j]);
		}
		fprintf(pFile,"\n");
		fclose(pFile);

		//Create the name_index file
		strcpy(filename,initMap["HomeDir"].asStr);
		strcat(filename,"/");
		strcat(filename,"name_index");
		pFile=fopen(filename,"a");
		fprintf(pFile,"%d=",index);
		fprintf(pFile,"%s",storeData.fileName);
		fprintf(pFile,"\n");
		fclose(pFile);

		//Create the nonce_index file
		strcpy(filename,initMap["HomeDir"].asStr);
		strcat(filename,"/");
		strcat(filename,"nonce_index");
		pFile=fopen(filename,"a");
		fprintf(pFile,"%d=",index);
		for(j=0;j<20;j++)
		{
			fprintf(pFile,"%02x",(uint8_t)storeData.fileNonce[j]);
		}
		fprintf(pFile,"\n");
		fclose(pFile);

		//Create the sha1_index file
		strcpy(filename,initMap["HomeDir"].asStr);
		strcat(filename,"/");
		strcat(filename,"sha1_index");
		pFile=fopen(filename,"a");
		fprintf(pFile,"%d=",index);
		for(j=0;j<20;j++)
		{
			fprintf(pFile,"%02x",(uint8_t)storeData.fileSha1[j]);
		}
		fprintf(pFile,"\n");
		fclose(pFile);
		return 0;
	}
}

void fillMsgHdrStructure(msg_header &hdr, char* buf)
{
	memcpy(&hdr.msgtype, &buf[0], sizeof(uint8_t));
	memcpy(&hdr.uoid, &buf[1], 20);
	memcpy(&hdr.timetolive, &buf[21], sizeof(uint8_t));
	memcpy(&hdr.reserved, &buf[22], sizeof(uint8_t));
	memcpy(&hdr.datalength, &buf[23], sizeof(uint32_t));
}

void fillMsgHdrBuffer(msg_header &hdr, char* buf)
{
	memcpy(&buf[0], &hdr.msgtype, sizeof(uint8_t));
	memcpy(&buf[1], &hdr.uoid,  20);
	memcpy(&buf[21], &hdr.timetolive, sizeof(uint8_t));
	memcpy(&buf[22], &hdr.reserved, sizeof(uint8_t));
	memcpy(&buf[23], &hdr.datalength, sizeof(uint32_t));
}
